Design Network Telemetry & Monitoring
- Design a platform that collects metrics from hundreds of thousands of devices and alerts fast
- Make the call the round turns on: polling counters versus devices pushing streaming telemetry
- Go deep on the cardinality explosion and the alert storm when a core link fails
Understanding the problem
Design a system that collects telemetry from a large fleet of network devices (routers and switches, plus firewalls and wireless access points) and turns it into monitoring. Each device reports interface counters and device health: CPU and memory, temperature, link and protocol state, error rates on every port. The platform has to ingest those metrics from hundreds of thousands of devices on a short interval and store the history as time-series. Operators query and chart any of it. And an alert fires the moment something crosses a threshold or starts behaving strangely. Handle the write firehose and the storage growth, then the failure modes and the alert storms.
This is the design that reads as most recognizably Cisco, and it helps to see why before touching a single box. Cisco's business is the network itself, the routers and switches and access points that carry the traffic, plus the software that watches over them. A platform that pulls health and counters off a huge fleet, then keeps that history and pages someone when a link drops, is not a hypothetical here. It is ThousandEyes. It is the Meraki dashboard. It is the observability layer inside their own products. The interviewer is not checking whether you can recite a generic ingestion diagram. They want to know whether you understand the shape of a system they ship.
How much the design round weighs depends on the level you are chasing. For sub-senior candidates it is a light touch, a few concept checks folded into a technical screen. From senior up it turns into a genuine, level-gating conversation. It usually opens on a system you have actually shipped, then pivots to a prompt like this one, with the interviewer acting as a fellow architect rather than an examiner. The prompt starts manageable and grows under you. Now it is not a hundred devices but two hundred thousand. Now a core link just failed and everything behind it went dark. Now the store is falling over. Expect the push to land on the parts a networking company feels in its bones. The firehose of writes. The cardinality of the data. And what a monitoring system does during the exact outage it exists to catch.
Functional requirements
Keep the core small and let the interviewer stretch it. There are four jobs. First, ingest metrics from a large fleet on a short interval: interface counters, CPU and memory, temperature, protocol and link health. Second, store that history as time-series and keep it queryable over a long retention window, with older data downsampled to stay affordable. Third, let an operator query any metric and chart it, sliced however the data is labeled, by device or by interface or across a whole region. Fourth, evaluate alert rules continuously and notify the right people fast. One requirement carries more weight than it looks. The platform has to accept both pull and push. A real fleet is always a mix: some devices stream their own telemetry, and others you still have to poll.
In scope
- Ingest metrics from hundreds of thousands of devices on a short interval: interface counters, CPU and memory, temperature, protocol and link health
- Store the history as time-series over a long retention window, with older data rolled up so the storage bill stays bounded
- Let an operator query any metric and chart it on a dashboard, sliced by device, interface, or region
- Evaluate alert rules on a stream: static thresholds, rate-of-change, and simple anomaly detection, then notify the right people in seconds
- Accept both pull (poll a device over SNMP) and push (a device streams its own telemetry), because a live fleet always mixes the two
Below the line (name it, then move on)
- The device firmware that produces the counters. We take the SNMP and streaming-telemetry feeds as given and design the platform that consumes them
- Deep packet inspection and per-flow records (NetFlow-style). We watch device and interface health, not the contents of every packet
- The remediation itself, reconfiguring a router or rerouting traffic. We detect and alert; acting on the alert is a separate control system
- Offline data-warehouse analytics and long-range capacity planning. The hot path here is near-real-time monitoring, not batch reporting
- Inventory and config management (which devices exist, their firmware versions). We consume the device list; owning it is its own system
Non-functional requirements
Write throughput leads, because the defining fact of this system is a relentless append of new samples from a vast fleet, and everything downstream has to keep up with it. Retention comes next, and it is really a cost target: keep a long history without keeping every sample at full resolution forever, which means downsampling old data. Then availability, framed as graceful degradation, because a fleet this size always has something broken. A collector process will die, a device group will drop off, and the platform has to keep ingesting the healthy majority while that heals. Last is alert latency. A link goes down and someone should know in seconds, not on the next slow polling cycle, so the alerting path has to run on a much tighter clock than storage or query.
Bank this slack now: a single sample is cheap and slightly lossy is fine. Miss one reading of an interface counter on a ten-second stream and the next one arrives ten seconds later, carrying the same running total, so nothing is truly lost. That means under pressure the platform can drop a reading, sample fewer of them, or downsample the history, all without breaking its promise. Per-sample exactness is not the point. Keeping up with the firehose and paging fast are. We will spend that slack deliberately later, on downsampling old data and on letting a queue shed a burst rather than stalling ingestion.
- ~200K devices monitored. Routers and switches, plus firewalls and access points, across an enterprise or service-provider estate. Illustrative teaching figure, not an audited count.
- ~10M/sec samples ingested at peak. A hundred million series on a ten-second streaming interval. This is the write firehose the whole design has to absorb.
- ~100M active time-series. Device times interface times counter. The cardinality figure, and the number that decides whether the store survives.
- seconds alert latency target. A link fails and the page should land in seconds, which is why alerting runs on the stream and not on a scheduled query.
Sizing the firehose
Put the firehose on the board before the architecture, because its size dictates almost every later choice. Treat every figure as an illustrative teaching estimate and derive it out loud, since a good interviewer redoes the arithmetic with you. Start with two hundred thousand devices. A mid-size access switch has forty-eight ports, and each port carries a handful of counters worth watching: bytes in and out, packets, errors, discards, utilization, call it ten per interface. Forty-eight ports is roughly four hundred and eighty interface counters. Another twenty or so device-level metrics (CPU, memory, temperature, a few protocol neighbor states) round it to about five hundred series per device. Multiply. Two hundred thousand devices at five hundred series each is one hundred million distinct time-series. Hold onto that number. It comes back to bite twice, once in storage and once in alerting.
Now turn series into a write rate, which is exactly where pull and push part ways. Poll every series once every thirty seconds, the old SNMP cadence, and you are writing a hundred million samples every thirty seconds, about three and a third million a second. Push instead on a ten-second streaming interval and it is ten million a second. Ask for one-second granularity on everything and it is a hundred million a second, which is the figure that explains why nobody streams every counter at one second. The interval is a dial that swings the write rate by an order of magnitude. Choosing it per metric, fast for the counters that matter and slow for the ones that rarely move, is one of the first levers you reach for.
- The write firehose: ~10M samples/sec. a hundred million series on a ten-second interval. It is append-heavy and timestamped and never stops, which is why a relational database is the wrong tool and a queue sits in front of storage.
- Cardinality: ~100M active series. device times interface times counter. Add one careless label (per-flow, per-neighbor) and it multiplies again. This is the number that quietly kills a naive store.
- Storage growth: ~1.7 TB/day compressed. ten million samples a second even at a couple of bytes each. Keeping every sample at full resolution forever does not scale, so old data is rolled up.
- Reads vs writes: writes steady, reads spike. ingestion is a constant firehose; a dashboard load or an incident fires a burst of heavy range queries. A big query must never stall ingestion.
Two lines in that grid set the agenda. The firehose has to be ingested and stored cheaply, and the two traps hiding inside it, cardinality and the alert storm, are where a networking system either holds or breaks. So the design keeps a few things true at once. The write path stays cheap and mostly hands-off. The store stays bounded as the fleet grows. And a single correlated failure never turns a steady stream of data into a flood of pages.
The set-up
Core entities
Name the objects before drawing anything. Four carry this design, and the one to pin down first is the series, because its count is the cardinality that governs the whole store.
Device
One monitored network element: a router or switch, a firewall, a wireless access point. It has an identity and a region and a role, plus a set of interfaces. It is the unit the collectors shard on and the label most queries filter by, so almost every question an operator asks starts from a device or a group of them.
Metric (a time-series)
A named measurement on a device or one of its interfaces: input octets on port 12 of switch A, CPU on router B. Each unique device-plus-interface-plus-metric-name combination is one series. The count of those series is the cardinality, and it is the single number the storage design lives or dies on.
Sample
One timestamped reading of a series: a value and the instant it was taken. Millions of these arrive every second. A single sample is disposable, replaced by a fresher one on the next interval; the stream of them is the product.
Alert rule
A condition evaluated continuously over the incoming samples: a threshold (CPU over ninety percent), a rate-of-change (errors climbing fast), or an anomaly (this interface sits far outside its usual band). There are only thousands of them rather than millions, and they change rarely, and when one fires the right people are notified.
Pin the stores next, because the interviewer will ask where each piece lives. Samples land in a time-series database, keyed by series and ordered by time, because that is the one access pattern that matters: append the newest point, read a range for one series. The alert rules and their firing state live in a small durable store the alert engine reads, since there are only thousands of rules and they change rarely. A series index maps a human query ("every interface on devices in Frankfurt") to the underlying series ids, so a dashboard resolves a filter without scanning everything. The message queue in the middle, Kafka, is not a store at all. It is a buffer that decouples the collectors from the database, so a slow write never pushes back onto a device.
API
Keep the external surface thin, and let the shape of each call say which regime it belongs to. Ingestion is not a request per sample; it is a long-lived stream from a device or a poll the collector drives. The query side is a range read by time. Reference these by name in the deep dives.
Two of these carry the design. The streaming ingest is the firehose. Everything about it exists to move millions of samples a second without the platform asking for each one: the device pushes and the collector simply receives, so the cost per sample collapses. The query call is the mirror image: a range read by time that a dashboard fires in spiky bursts, and a broad one can starve writes if the two are not kept apart. The alert notification is the payoff of the whole system. Its note hides the hardest problem in the design: what "grouped and deduplicated" has to mean when a single failure trips ten thousand rules at once.
High-level design
Follow the data one hop at a time. Metrics leave the Network devices two ways, pushed on a stream or pulled by a poll, and reach the Collector fleet, which normalizes them and publishes onto the Telemetry queue. From that queue two consumers read the same records: the Time-series store writes them down, and the Alert engine evaluates them against its rules in near real time. Operators reach the history through Query + dashboards, which range-reads the store, and a firing alert leaves through On-call + webhooks. The diagram colors each box by role: blue for the devices and the on-call destination, violet for services, amber for the queue, green for the store. The walkthrough reuses the same tags.
1) Devices report, two ways. The Network devices hand over metrics either by streaming them (the device pushes on a subscription over gRPC dial-out) or by being polled (the collector drives an SNMP round-trip on a timer). Both land at the Collector fleet. Keeping both paths is deliberate, because the fleet is a mix and the platform cannot pretend every device can stream. The bottleneck being set up here, and defused in the first deep dive, is that this ingest path carries the entire firehose.
2) Collectors normalize and buffer. Each collector in the Collector fleet owns a shard of the fleet, turns whatever it receives into a common sample shape, and publishes onto the Telemetry queue. The queue matters more than it looks. It decouples ingest from storage, so a burst or a slow database queues durably in the log instead of pushing back onto the devices or dropping on the floor. What is punted here is how the collectors divide the fleet and survive one of their own dying, which the last deep dive owns.
3) The store writes the firehose. The Time-series store consumes the Telemetry queue and appends each sample to its series, ordered by time. This is where retention and cost get decided. Recent data stays at full resolution on fast disk, old data is rolled up into coarser aggregates, and the whole thing is partitioned so it scales by adding nodes. The trap being deferred is cardinality, the number of distinct series, which is the second deep dive.
4) The alert engine evaluates on the stream. In parallel with the store, the Alert engine reads the same Telemetry queue and checks samples against its rules as they arrive. A breach is seen seconds after it happens rather than on the next scheduled query. It never touches the storage database, which keeps alerting and ingestion from fighting over the same resource. The hard part it defers is what to do when one failure trips ten thousand rules in the same instant, which the third deep dive is all about.
5) Operators read the history. Query + dashboards resolves a filter through the series index and range-reads the Time-series store for a chart or an investigation. This path is spiky, quiet until an operator opens a dashboard or an incident lights up a room, and then it fires a burst of heavy queries at the worst moment. Keeping that burst from stalling the write firehose, by isolating the read tier, is a thread the last deep dive picks up.
Technology choices
A labeled box proves nothing on its own. Back it with a real tool, argue why that kind of tool suits this workload, and know where it buckles under this load. Slot a mainstream choice into each one, and the follow-ups will all come from the failure modes.
- Telemetry queue is a durable log: Kafka, partitioned so the load spreads across brokers and each partition keeps its records in order. A log fits because ingest is bursty and has two independent consumers at different speeds. The store and the alert engine each track their own offset and replay after a crash. The failure story is a consumer falling behind. It doesn't lose data; it reads older offsets and catches up. Telemetry tolerates a little loss, so if a consumer is badly behind, the oldest samples can age out of retention without breaking anything.
- Time-series store is a purpose-built time-series database: Prometheus, InfluxDB, or TimescaleDB, chosen because the access pattern is append the newest point and read one series by range. This category compresses timestamps and slowly-changing values into a couple of bytes each and serves range-by-time reads cheaply, which is exactly the workload. The failure mode to name is cardinality: memory and index scale with the number of active series, so a hundred million series is a design constraint and a careless label is an outage.
- The ingest transport is gRPC dial-out streaming telemetry for the push path and SNMP for the pull path. Streaming is right at this scale because the device sends on its own, so the platform stops paying a round-trip per sample and the interval can drop toward seconds. SNMP stays because a large slice of any real fleet only speaks it. The failure to plan for is a reconnect storm when a group of devices comes back at once and all re-establish their streams, which the collectors and the queue have to absorb.
- Alert engine is a streaming rule evaluator. It's a stateful stream processor (a Flink or Kafka-Streams style consumer, or the streaming-alerting core of a monitoring stack) that holds each rule's state in memory and evaluates against the sample flow. Streaming is right because the alert latency budget is seconds and a scheduled query cannot meet it. The failure mode is the alert storm, and the answer is the grouping and inhibition layer, which is where this component earns its place.
Potential deep dives
Everything still on the table is won here, in the dives. A telemetry round almost always pushes on the same four corners. How do the metrics get in, where do the time-series live, how does alerting stay sane, and what happens at full scale when a piece fails? The two traps a networking company reaches for fastest are cardinality and the alert storm, so those get the most room.
1) Ingestion at scale: from polling to streaming telemetry
Start at the wire, because how a device hands over its metrics is the choice the modern version of this system turns on. For decades the answer was polling: a monitoring server walks a list of devices and asks each one for its counters on a timer, usually over SNMP. It works, and plenty of the fleet still speaks only that language. But polling hundreds of thousands of devices every few seconds runs into a wall, and the industry has been moving the other way, toward letting the device push its own telemetry. Walk the arc, because the interviewer wants to see you arrive at push for the right reasons rather than just name it.
Approach
Run a single monitoring server that holds the device list and polls every device on a fixed timer. Each interval it opens an SNMP session to each device, walks the counters it cares about, reads them back, then writes them down. It is the classic network-management-station design. It is simple, and for a few hundred devices it is genuinely fine.
Challenges
It falls over well before the fleet does. One machine polling two hundred thousand devices every thirty seconds has to open and drain millions of SNMP round-trips per cycle, and it runs out of CPU and sockets and raw clock time. Polling is synchronous per device, so one slow or unreachable device stalls the walk behind it. And it is a single point of failure: when that server dies, the entire fleet goes dark at once. You cannot push a firehose through one straw.
Approach
Split the device list across many collector processes. Each collector owns a slice of the fleet, a few thousand devices, and polls only its slice, so the work fans out across machines and grows by adding collectors. A coordinator assigns devices to collectors and reassigns them when one dies. This is the right scaling instinct, and it is how a lot of real polling systems are built.
Challenges
Sharding fixes the throughput ceiling but not the cost of polling itself. Every sample still costs a round-trip the collector has to initiate. So the collectors spend most of their effort asking for data that mostly has not changed. And the interval is bounded by how fast a collector can walk its slice. You also load the devices: a switch spending cycles answering SNMP walks is a switch not spending them forwarding packets. Polling faster for fresher data makes both problems worse.
Approach
Flip the direction: each device streams its own telemetry. It subscribes to the metrics it should report and pushes them out on an interval or on change, over a streaming transport like gRPC dial-out, to a collector that simply receives. The collector fleet is still sharded, but now it terminates streams instead of driving polls, which is far cheaper per sample and scales to a much shorter interval. Put a queue, Kafka, between the collectors and storage: the collectors publish normalized samples onto the log and the database consumes at its own pace. That queue is also where back-pressure lives, so when a burst outruns the writers it buffers in the log rather than pushing back onto the devices or dropping. Keep polling for the legacy devices that cannot stream, so the platform ingests both.
Challenges
Push moves state onto the devices and the transport. A device now holds a subscription and a live connection, so you manage which device streams what. Plan for a reconnect storm when a group of devices returns at once and re-establishes every stream together. Streaming also arrives in bursts, spiky where polling was a steady metronome, which is exactly why the queue in front of storage is not optional. The write path stops paying a round-trip per sample, and the interval drops from tens of seconds toward seconds. The collectors and the database scale on their own clocks, because the log sits between them.
Polling asks; streaming tells, and at fleet scale telling wins. Move from a central poller to a sharded collector fleet, then flip pull to push with streaming telemetry, and drop a queue between ingest and storage. The write path stops paying a round-trip per sample, and back-pressure buffers in the log instead of on the devices. Keep the poll path alive for the devices that only speak SNMP. The queue in the middle is what lets the firehose and the database scale on separate clocks.
2) Time-series storage and the cardinality trap
Now the firehose has to land somewhere, and the instinct to reach for a familiar relational database is the first thing to talk yourself out of. The access pattern here is narrow and peculiar. Writes are almost all appends of the newest point, reads are almost all a range of one series over a time window, and there are a hundred million series. A store built for that looks nothing like a store built for transactional rows. Walk why, because the reason a time-series database exists is the reason your first instinct is wrong.
Approach
Model each sample as a row: a timestamp, a device id, a metric name, then a value, with an index on time and device. It is the schema every engineer knows, anyone can query it in SQL, and for a small deployment it works.
Challenges
It collapses under both the write rate and the read pattern. Ten million inserts a second is far past what a single relational instance sustains. Every insert also updates a B-tree index, which turns a clean append into random writes scattered across the disk. The table grows without bound, a query over a time window scans and sorts enormous row counts, and the index for a hundred million series is itself huge. A relational database is built to update and join rows transactionally, and you are doing neither. You are appending timestamped points and reading them back by range.
Approach
Reach for a purpose-built time-series database: Prometheus or InfluxDB or TimescaleDB, the category built for exactly this. It stores points by series, ordered by time, so an append is a cheap write to the head of a series and a range read is a sequential scan. It compresses hard, because consecutive timestamps and slowly-changing values pack into a couple of bytes each with delta and XOR encoding. The write rate and the range reads both stop hurting.
Challenges
The category is right, but keeping everything at full resolution forever brings two costs the naive version ignores. Storage grows without limit. Ten million samples a second at a couple of bytes is well over a terabyte a day, and a year of that is enormous and mostly never read at full detail. And the store is now exposed to cardinality, because its memory and index scale with the number of active series, not the number of samples. A hundred million series is already a lot. One careless label makes it a billion, and the store falls over.
Approach
Keep the time-series database, then manage what it holds. Downsample by age. Keep raw samples for a short window of days, then roll them into one-minute aggregates for weeks and one-hour aggregates for a year or more. Nobody debugging a six-month-old trend needs per-second detail. Partition by time, so a day is a self-contained chunk you can compact or query or drop as a unit, and by series, so writes and reads spread across nodes. Push cold rollups to cheap object storage while the hot recent window stays on fast disk. And treat cardinality as a budget you enforce. Bound which label combinations are allowed, and keep high-churn labels (per-flow, per-session) out of the series key. Watch for a device or an integration that starts minting new series without limit.
Challenges
Rollups mean you decide up front which aggregates matter, the max and the average and the ninety-fifth percentile, because you cannot recompute a percentile you never kept once the raw samples age out. Retention tiers add a compaction pipeline that runs continuously and has to keep pace with ingestion. Cardinality discipline is a standing fight. Every new metric, label or integration can multiply the series count, so the platform needs guardrails and visibility into its own cardinality. These are real operational costs, and they are the price of a store that stays bounded while the fleet grows.
A monitoring store is not a relational database with timestamps bolted on. It is built for one access pattern: append the newest point, read one series by range. Use a time-series database, compress hard, downsample old data into rollups across retention tiers, and partition by time and series. But guard cardinality above everything, because the store scales with the number of series, not the number of samples. The fleet that multiplies devices by interfaces by counters is exactly the fleet that discovers this the hard way.
3) Alerting: stream evaluation and the alert storm
Storage lets you look at the past; alerting is what pages someone about the present, and it runs on a tighter clock. An alert that fires two minutes after a core link drops is nearly useless. Evaluation has to happen on the stream of samples as they arrive, ahead of any query that trails behind. There are three shapes of rule worth supporting, and one failure mode that dominates the entire dive.
A threshold is the simplest rule: fire when a value crosses a line, CPU over ninety percent, an interface past eighty percent utilization, temperature over a limit. A rate-of-change rule catches trouble a static line misses, like input errors on an interface climbing fast even while the absolute count is still low, because the slope is the early warning. An anomaly rule compares a series against its own recent baseline and fires when it drifts far outside its usual band. It catches the problems nobody thought to write a threshold for. All three are cheap to evaluate on one series. The hard part is what happens when thousands of series trip in the same instant.
Approach
Run each alert rule as a query against the time-series database on a timer: every minute, ask the store whether any device is over the threshold, and page if so. It reuses the query path you already built, and it is easy to reason about.
Challenges
It is too slow and it hammers the store. A one-minute evaluation loop means a link can be down for most of a minute before anyone knows, which defeats near-real-time. Running thousands of rules as repeated range queries loads the same database that is already absorbing the write firehose. Alerting and ingestion then fight over one resource exactly when an incident is spiking both. You have coupled the thing that must stay fast to the thing that is already busiest.
Approach
Move evaluation onto the stream. As samples flow through the queue, a rule engine consumes them and checks each relevant series against its rules in memory. A breach is caught within a sample or two of happening, in seconds. It never touches the storage database, so alerting and ingestion stop competing. Each rule keeps a little state per series, the current value and a short window for rate-of-change, and fires the moment a condition trips.
Challenges
Stream evaluation is fast and correct per series, but it is naive about correlation. Every breaching series fires its own alert. A rule that trips on one flapping interface is fine. That same rule, when a core link fails and ten thousand devices go unreachable at once, produces ten thousand alerts in the same second. It also has no memory of what it already told you: a value bouncing across the threshold fires again on every crossing, so one unstable interface can page someone every few seconds. Per-series correctness is not the same thing as a sane page.
Approach
Add a layer between detecting a condition and notifying a human. Deduplicate: collapse repeated firings of the same alert into one active incident that stays open until it clears. Suppress flapping with hysteresis: fire only after a condition holds for a short dwell time, and clear only after it recovers for one, so a value hovering on the line doesn't machine-gun pages. Group related alerts, so ten thousand interface-down alerts that share a cause and a time window become one incident instead of ten thousand. Inhibit downstream alerts under a known upstream cause, so when a core router goes down the flood of unreachable-device alerts behind it is silenced beneath the one alert that matters. Then fan out the survivors to the right destinations by severity and on-call schedule.
Challenges
Grouping and inhibition need a notion of relationship the raw metrics don't carry. Feed the engine some topology or dependency hints (this device sits behind that uplink) so it knows which alerts roll up under which cause. Dwell times trade speed for calm: too long slows a real page, too short lets the flapping leak through, so you tune them per rule. And dedup needs a stable identity for an alert so two firings are recognized as the same incident. This is the machinery that turns a correct detector into a system an on-call engineer can actually live with.
Detecting a breach is the easy half; not drowning the on-call is the hard one. Evaluate threshold, rate-of-change and anomaly rules on the stream, so a page is seconds behind reality and never touches the storage database. Put dedup, flap-suppression, grouping and inhibition between detection and notification so one correlated failure becomes one incident. The ten-thousand-devices-down storm is the case the whole alerting design exists to survive, and rolling the flood up under its shared cause is what turns a useless alarm into an actionable page.
4) Scale and failure: the firehose, a dead collector, query isolation
The last dive is the staff-level one: what the numbers actually are, and what breaks when a piece falls over. Re-derive the firehose so it is on the board, because every failure conversation references it. A hundred million series on a ten-second interval is ten million samples a second, steady, all day and all night. That single figure explains three design facts. A queue sits in front of storage, the store is a horizontal cluster rather than one box, and a broad query is dangerous. No single machine is the answer to anything here, so the design is a set of independently scaled tiers with a buffer between them.
Take query isolation first, because it is the one people forget. Ingestion is a relentless, predictable firehose. Queries are the opposite: mostly quiet, then spiky. An operator opening a dashboard or an incident lighting up a war room fires a burst of heavy range queries at once, exactly when ingestion is also peaking. If reads and writes share the same nodes, one expensive query can starve the ingest path. A year of data across every interface in a region is enough to do it. The platform then falls behind on writes during the incident you most need it standing. So you isolate them. Route queries to read replicas or a separate query tier that reads the same storage, cap and time out expensive queries, and keep the write path on nodes a dashboard cannot reach. Ingestion must never lose to a dashboard.
Now the collectors, because a dead collector is the most common failure and the interviewer will ask how the fleet divides work and heals. The question underneath is how much has to move when one collector joins or leaves. The answer separates a design that stampedes from one that barely notices.
Approach
Assign devices to collectors once, by a fixed rule (device id modulo collector count, or a hand-maintained map), and leave it. Each collector owns its fixed slice and never changes. Simple, and nothing moves at runtime.
Challenges
A static map does not survive change. When a collector dies, its slice of the fleet goes uncollected until someone intervenes, so a chunk of the network goes dark. When you add a collector to grow, the modulo changes and nearly every device reshuffles to a new owner at once, a stampede of re-subscriptions across the whole fleet. Static assignment cannot handle the two things that always happen: failure and growth.
Approach
Put a coordinator in charge of the mapping. It tracks which collectors are alive by heartbeat, holds the device-to-collector assignment, and when a collector stops heartbeating it reassigns that collector's devices to the survivors. Devices re-establish their streams to the new collector, and the gap is bounded by how fast a death is detected. Growth is handled the same way, by handing some devices to a new collector.
Challenges
This works, and it's a common shape. But the coordinator is now a critical component you have to make reliable, and a full reshuffle on every membership change is still disruptive if the assignment is naive. The question the interviewer pushes on is exactly how much moves when one collector joins or leaves. Reassigning that collector's share is fine. Reshuffling the entire fleet is not. That is the cue for the last refinement.
Approach
Assign devices to collectors by consistent hashing, the same rebalancing a Kafka consumer group does. Each collector owns a range of a hash ring, and a device maps to the collector that owns its hash. When a collector dies, only its range is reassigned to its neighbors, so roughly one collector's worth of devices move rather than the whole fleet. When you add a collector, it takes a slice from its neighbors and everyone else stays put. A coordinator still watches liveness and drives the rebalance, but the blast radius of any single change is one shard. Size the collectors so losing one is a small fraction of the fleet briefly re-homing.
Challenges
Consistent hashing bounds the movement but does not make it free: the devices that do move still drop and re-establish their streams, so a rebalance is a small reconnect burst you plan for. And you accept slightly uneven shards, because a hash ring is never perfectly balanced, in exchange for stability. The trade is deliberate. You get a bounded, predictable re-homing on every failure and every scale event, instead of a fleet-wide stampede whenever membership changes.
- A collector dies: one shard re-homes. consistent hashing moves only that collector's devices to its neighbors; the coordinator drives the rebalance and the rest of the fleet never moves.
- The queue absorbs bursts: buffer, do not drop. a streaming burst or a writer slowdown queues durably in Kafka instead of pushing back onto devices, so ingestion smooths out rather than shedding.
- Query vs ingest: isolate the paths. reads go to a separate query tier or replicas, expensive queries are capped, and the write path stays on nodes a dashboard cannot reach.
- The store scales out: shard by series + time. no single node holds the firehose; series and time partitioning spread the writes, and old partitions compact or drop as whole units.
At ten million samples a second, no single machine is the answer to anything, so the design is a set of independently scaled tiers with a buffer between them. A collector death re-homes one shard through consistent hashing rather than stampeding the fleet. The queue absorbs bursts instead of dropping them or pushing back onto devices, and queries run on a tier that can never starve ingestion. The firehose is relentless and predictable. The whole job is keeping any one failure, or any one heavy query, from turning that steady pressure into a backlog.
What is expected at each level
This design carries a lot of surface, and the rungs separate on how much of it you own before anyone asks. Below senior the round is a light touch. At senior and above it becomes a gating discussion, usually leading with a system you've operated before it turns to a prompt like this one. Read the rungs as a climb: a working pipeline at mid, the pull-to-push shift and the cardinality trap owned at senior, and the alert storm and the failure story carried at staff.
Mid-level engineer
Spend your time on breadth, about eighty to twenty, and reach one coherent end-to-end path. Devices report metrics to a collector fleet, the collectors publish to a queue, a time-series database stores the samples, and an alert engine and a dashboard read from there. Name the right store, a time-series database and not a relational one, and be able to say why the access pattern demands it. The phase that earns the pass is scale. When the interviewer asks what happens at hundreds of thousands of devices, reason toward sharding the collectors and putting a queue in front of storage rather than one server doing it all. A working pipeline you can defend, with the interviewer filling in the harder corners, clears this rung.
Senior engineer
Now the balance tips to depth, roughly sixty to forty, and this is the level the round is calibrated to. Raise the hard facts yourself. Derive the firehose out loud, devices times series over the interval, and use it to justify the queue and the horizontal store. Make the pull-versus-push call and explain why streaming telemetry beats polling at this scale, while keeping SNMP as the path for the devices that need it. And surface cardinality without being asked: the store scales with the number of series, and device times interface times counter is where a naive design dies. Reaching a near-complete design whose write path and store choice and cardinality risk you flagged on your own is the bar here.
Staff and beyond
At this level depth dominates the whole picture, about forty to sixty. Own the alert storm end to end: a correct stream detector produces ten thousand pages when a core link fails, and grouping plus inhibition is what collapses that into one actionable incident. Carry the failure story, a collector death re-homing one shard through consistent hashing, the queue absorbing bursts, query and ingest isolated so a dashboard never stalls the firehose. Hold retention tiers, rollups and cardinality discipline as deliberate levers, and know how an operator changes a rule, watches a device pinned at a threshold, and sees the store's own cardinality climbing. You drive the design, and the interviewer course-corrects instead of stepping in to save it. The bar is holding ingestion, storage, alerting and failure as four separate levers while keeping write throughput and near-real-time alerting intact through every refinement.
Key Takeaways
- ✓A network monitoring platform is a firehose problem: hundreds of thousands of devices times hundreds of series each, landing as millions of timestamped samples a second. Derive that number by hand early, because it justifies almost every later choice: a sharded collector fleet, a queue in front of storage, then a horizontally partitioned store.
- ✓The modern shift is pull to push. Polling every device over SNMP has a floor you can't get under, so let devices stream their own telemetry over gRPC dial-out. Keep a sharded collector fleet to receive it, and put a queue between ingest and storage so back-pressure buffers in the log instead of on the devices. Keep the poll path for the devices that only speak SNMP.
- ✓Store samples in a time-series database, never a relational one, and guard cardinality above all. The store scales with the number of series, and device times interface times counter is the product that explodes. Compress hard, downsample old data into rollups across retention tiers, and partition by time and series so the store stays bounded as the fleet grows.
- ✓Alerting runs on the stream, seconds behind reality, and its real job is the storm. When one core link fails and ten thousand devices go unreachable at once, dedup, flap-suppression, grouping and inhibition turn ten thousand true-but-useless alerts into one actionable page. That, plus a collector death re-homing only one shard, is the failure story a networking loop is really testing.
Cisco interview guideKnow what Cisco asks before you walk in.
19 coding · 4 system design · 15 behavioral questions
Get their questions first.
InterviewLegend is independent and not affiliated with or endorsed by any company it covers.